/**
 * Copyright 2018-2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.util.vfs;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.jianggujin.modulelink.util.JLogFactory;
import com.jianggujin.modulelink.util.JLogFactory.JLog;

/**
 * 一个用于JBoss6的{@link JVFS }实现
 * 
 * @author jianggujin
 *
 */
public class JJBoss6VFS extends JVFS {
    private static final JLog logger = JLogFactory.getLog(JJBoss6VFS.class);

    /**
     * 模仿JBoss VirtualFile类的精简实现
     * 
     * @author jianggujin
     *
     */
    static class JVirtualFile {
        static Class<?> VirtualFile;
        static Method getPathNameRelativeTo, getChildrenRecursively;

        Object virtualFile;

        JVirtualFile(Object virtualFile) {
            this.virtualFile = virtualFile;
        }

        String getPathNameRelativeTo(JVirtualFile parent) {
            try {
                return invoke(getPathNameRelativeTo, virtualFile, parent.virtualFile);
            } catch (IOException e) {
                logger.error("This should not be possible. JVirtualFile.getPathNameRelativeTo() threw IOException.");
                return null;
            }
        }

        List<JVirtualFile> getChildren() throws IOException {
            List<?> objects = invoke(getChildrenRecursively, virtualFile);
            List<JVirtualFile> children = new ArrayList<JVirtualFile>(objects.size());
            for (Object object : objects) {
                children.add(new JVirtualFile(object));
            }
            return children;
        }
    }

    /**
     * 模仿JBoss VFS类的精简实现
     * 
     * @author jianggujin
     *
     */
    static class JVFS {
        static Class<?> VFS;
        static Method getChild;

        private JVFS() {
        }

        static JVirtualFile getChild(URL url) throws IOException {
            Object o = invoke(getChild, VFS, url);
            return o == null ? null : new JVirtualFile(o);
        }
    }

    /**
     * 表示VFS当前的环境是否有效
     */
    private static Boolean valid;

    /**
     * 找到所有需要访问JBoss 6 VFS的类和方法
     */
    protected static synchronized void initialize() {
        if (valid == null) {
            // 假设有效。如果出错，它会被翻转
            valid = Boolean.TRUE;

            // 查找和验证所需的类
            JVFS.VFS = checkNotNull(getClass("org.jboss.vfs.VFS"));
            JVirtualFile.VirtualFile = checkNotNull(getClass("org.jboss.vfs.VirtualFile"));

            // 查找和验证所需的方法
            JVFS.getChild = checkNotNull(getMethod(JVFS.VFS, "getChild", URL.class));
            JVirtualFile.getChildrenRecursively = checkNotNull(
                    getMethod(JVirtualFile.VirtualFile, "getChildrenRecursively"));
            JVirtualFile.getPathNameRelativeTo = checkNotNull(
                    getMethod(JVirtualFile.VirtualFile, "getPathNameRelativeTo", JVirtualFile.VirtualFile));

            // 验证API没有改变
            checkReturnType(JVFS.getChild, JVirtualFile.VirtualFile);
            checkReturnType(JVirtualFile.getChildrenRecursively, List.class);
            checkReturnType(JVirtualFile.getPathNameRelativeTo, String.class);
        }
    }

    /**
     * 验证所提供的对象引用是否为null。如果它是空的，那么VFS在当前环境终将被标记为无效
     * 
     * @param <T>    需要检测的对象的泛型参数
     * @param object 需要检测的对象
     * @return 不为空则返回检测的对象
     */
    protected static <T> T checkNotNull(T object) {
        if (object == null) {
            setInvalid();
        }
        return object;
    }

    /**
     * 验证方法的返回类型是期望的类型。如果不是，那么VFS在当前环境终将被标记为无效
     * 
     * @param method   需要验证的方法
     * @param expected 期望的方法返回类型
     */
    protected static void checkReturnType(Method method, Class<?> expected) {
        if (method != null && !expected.isAssignableFrom(method.getReturnType())) {
            logger.error("Method " + method.getClass().getName() + "." + method.getName() + "(..) should return "
                    + expected.getName() + " but returns " + method.getReturnType().getName() + " instead.");
            setInvalid();
        }
    }

    /**
     * 标记VFS在当前环境是无效的
     */
    protected static void setInvalid() {
        if (JJBoss6VFS.valid == Boolean.TRUE) {
            logger.debug("JBoss 6 VFS API is not available in this environment.");
            JJBoss6VFS.valid = Boolean.FALSE;
        }
    }

    static {
        initialize();
    }

    @Override
    public boolean isValid() {
        return valid;
    }

    @Override
    public List<String> list(URL url, String path) throws IOException {
        JVirtualFile directory;
        directory = JVFS.getChild(url);
        if (directory == null) {
            return Collections.emptyList();
        }

        if (!path.endsWith("/")) {
            path += "/";
        }

        List<JVirtualFile> children = directory.getChildren();
        List<String> names = new ArrayList<String>(children.size());
        for (JVirtualFile vf : children) {
            names.add(path + vf.getPathNameRelativeTo(directory));
        }

        return names;
    }
}
